package ink.metoo.gude.module.game.service.impl

import ink.metoo.gude.handler.ApiException
import ink.metoo.gude.model.ApiStatus
import ink.metoo.gude.module.security.domain.ddl.User
import ink.metoo.gude.module.game.domain.ddl.GameRoom
import ink.metoo.gude.module.game.domain.ddl.GameType
import ink.metoo.gude.module.game.domain.dto.GameCreateDTO
import ink.metoo.gude.module.game.repository.GameRoomRepository
import ink.metoo.gude.module.game.service.GameCoreService
import ink.metoo.gude.module.game.service.GameService
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
class GameServiceImpl(
    private val gameRoomRepository: GameRoomRepository,
    gameCoreServices: List<GameCoreService>
) : GameService {

    private val gameCoreServiceMap = gameCoreServices.associateBy { it.gameType }

    val GameType?.gameCoreService: GameCoreService
        get() = gameCoreServiceMap[this] ?: throw ApiException(ApiStatus.G_5005)

    @Transactional
    override fun create(
        user: User,
        gameCreateDTO: GameCreateDTO
    ): GameRoom {
        val gr = GameRoom()
        val gameType = gameCreateDTO.gameType
        with(gameType.gameCoreService) {
            gr.init()
        }
        gr.creator = user
        gr.gameType = gameType
        return gameRoomRepository.save(gr)
    }

    private fun Long.getGameRoom(): GameRoom = gameRoomRepository.findById(this).orElseThrow {
        ApiException(ApiStatus.G_5006)
    }

    @Transactional
    override fun join(user: User, roomId: Long) {
        val gr = roomId.getGameRoom()
        when (gr.status!!) {
            GameRoom.Status.WAIT -> {
                val gameType = gr.gameType
                with(gameType.gameCoreService) {
                    user.join(gr)
                }
            }

            GameRoom.Status.IN_PROGRESS -> throw ApiException(ApiStatus.G_5001)
            GameRoom.Status.FINISH -> throw ApiException(ApiStatus.G_5002)
        }
    }

    override fun leave(user: User, roomId: Long) {
        val gr = roomId.getGameRoom()
        when (gr.status!!) {
            GameRoom.Status.WAIT, GameRoom.Status.IN_PROGRESS -> {
                val gameType = gr.gameType
                with(gameType.gameCoreService) {
                    user.leave(gr)
                }
            }

            GameRoom.Status.FINISH -> throw ApiException(ApiStatus.G_5002)
        }
    }

    override fun watch(user: User, roomId: Long) {
        val gr = roomId.getGameRoom()
        when (gr.status!!) {
            GameRoom.Status.WAIT, GameRoom.Status.IN_PROGRESS -> {
                val gameType = gr.gameType
                with(gameType.gameCoreService) {
                    user.watch(gr)
                }
            }

            GameRoom.Status.FINISH -> throw ApiException(ApiStatus.G_5002)
        }
    }


}